package com.zbkj.service.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.zbkj.common.exception.AppException;
import com.zbkj.common.model.admin.SystemAdmin;
import com.zbkj.common.model.merchant.Merchant;
import com.zbkj.common.model.product.Product;
import com.zbkj.common.model.traceability.Batches;
import com.zbkj.common.model.traceability.TraceableItems;
import com.zbkj.common.page.CommonPage;
import com.zbkj.common.request.PageParamRequest;
import com.zbkj.common.request.traceability.BatchesFilterRequest;
import com.zbkj.common.request.traceability.BatchesPageRequest;
import com.zbkj.common.request.traceability.BatchesRequest;
import com.zbkj.common.response.traceability.BatchesInfoResponse;
import com.zbkj.service.dao.traceability.BatchesDao;
import com.zbkj.service.service.BatchesService;
import com.zbkj.service.service.MerchantService;
import com.zbkj.service.service.ProductService;
import com.zbkj.service.service.TraceableItemsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 商品批次Service实现类
 * +----------------------------------------------------------------------
 * | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
 * +----------------------------------------------------------------------
 * | Author: xiaolic
 * +----------------------------------------------------------------------
 */
@Slf4j
@Service
public class BatchesServiceImpl extends ServiceImpl<BatchesDao, Batches> implements BatchesService {

    @Resource
    private BatchesDao batchesDao;
    
    @Autowired
    private TraceableItemsService traceableItemsService;
    
    @Autowired
    private ProductService productService;
    
    @Autowired
    private MerchantService merchantService;

    /**
     * 分页查询商品批次列表
     * @param request 分页请求参数
     * @return 分页结果
     */
    @Override
    public PageInfo<Batches> getPageList(BatchesPageRequest request) {
        LambdaQueryWrapper<Batches> queryWrapper = new LambdaQueryWrapper<>();
        // 只查询未删除的数据
        queryWrapper.eq(Batches::getIsDeleted, 0);
        
        // 按照批次名称模糊查询
        if (request.getBatchName() != null && !request.getBatchName().isEmpty()) {
            queryWrapper.like(Batches::getBatchName, request.getBatchName());
        }
        // 按照生产日期查询
        if (request.getProductionDate() != null) {
            queryWrapper.eq(Batches::getProductionDate, request.getProductionDate());
        }
        // 按照状态查询
        if (request.getStatus() != null) {
            queryWrapper.eq(Batches::getStatus, request.getStatus());
        }
        // 按照商品ID查询
        if (request.getProductId() != null) {
            queryWrapper.eq(Batches::getProductId, request.getProductId());
        }
        // 按照商家ID查询
        if (request.getMerchantId() != null) {
            queryWrapper.eq(Batches::getMerchantId, request.getMerchantId());
        }

        // 按照排序字段升序
        queryWrapper.orderByAsc(Batches::getSortOrder);
        // 默认按创建时间升序
        queryWrapper.orderByAsc(Batches::getCreatedAt);
        
        // 使用PageHelper进行分页
        Page<Batches> page = PageHelper.startPage(request.getPage(), request.getLimit());
        List<Batches> batches = batchesDao.selectList(queryWrapper);
        
        if (CollUtil.isEmpty(batches)) {
            return CommonPage.copyPageInfo(page, CollUtil.newArrayList());
        }
        
        // 返回分页结果
        return CommonPage.copyPageInfo(page, batches);
    }

    /**
     * 创建商品批次
     * @param request 创建请求参数
     * @return 创建结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean create(BatchesRequest request) {
        // 检查号段范围
        if (request.getStartSerial() > request.getEndSerial()) {
            throw new AppException("起始号段不能大于结束号段");
        }
        
        // 检查号段是否重叠
        Boolean isOverlap = checkSerialOverlap(request.getStartSerial(), request.getEndSerial(), request.getProductId(), request.getMerchantId(), null);
        if (isOverlap) {
            throw new AppException("号段范围与已有批次重叠，请调整号段范围");
        }

        String batchName = "";
        // 是否手动设置批次名称
        if (request.getIsManualSetBatchName()) {
            batchName = request.getBatchName();
        } else {
            // 自动生成批次名称，不再使用用户输入的名称
            LambdaQueryWrapper<Batches> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Batches::getProductId, request.getProductId());
            queryWrapper.eq(Batches::getMerchantId, request.getMerchantId());
            queryWrapper.eq(Batches::getIsDeleted, 0);
            queryWrapper.orderByAsc(Batches::getSortOrder);
            queryWrapper.orderByAsc(Batches::getCreatedAt);
            List<Batches> existingBatches = this.list(queryWrapper);
            int batchCount = existingBatches.size() + 1;
            batchName = "第" + batchCount + "批";
        }

        // 创建批次
        Batches batches = new Batches();
        BeanUtils.copyProperties(request, batches);
        // 自动设置批次名称为"第N批"
        batches.setBatchName(batchName);
        batches.setCreatedAt(new Date());
        batches.setUpdatedAt(new Date());
        
        // 保存批次信息
        boolean result = save(batches);
        if (!result) {
            throw new AppException("创建批次失败");
        }
        
        // 生成批次对应的可追溯项
        traceableItemsService.generateForBatch(batches.getId());
        
        return true;
    }

    /**
     * 更新商品批次
     * @param request 更新请求参数
     * @return 更新结果
     */
    @Override
    public Boolean update(BatchesRequest request) {
        if (request.getId() == null) {
            throw new AppException("批次ID不能为空");
        }
        
        // 查询原有批次
        Batches oldBatch = getDetail(request.getId());
        if (oldBatch == null) {
            throw new AppException("批次不存在");
        }
        
        // 检查号段范围
        if (request.getStartSerial() > request.getEndSerial()) {
            throw new AppException("起始号段不能大于结束号段");
        }
        
        // 如果号段有变化，检查是否重叠
        if (!oldBatch.getStartSerial().equals(request.getStartSerial()) || 
            !oldBatch.getEndSerial().equals(request.getEndSerial())) {
            Boolean isOverlap = checkSerialOverlap(request.getStartSerial(), request.getEndSerial(), request.getProductId(), request.getMerchantId(), request.getId());
            if (isOverlap) {
                throw new AppException("号段范围与已有批次重叠，请调整号段范围");
            }
        }
        
        // 更新批次
        Batches batches = new Batches();
        BeanUtils.copyProperties(request, batches);
        batches.setUpdatedAt(new Date());
        
        return updateById(batches);
    }

    /**
     * 删除商品批次
     * @param id 批次ID
     * @return 删除结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(Long id) {
        // 先获取当前批次信息，以便后续更新其它批次名称
        Batches currentBatch = getDetail(id);
        if (currentBatch == null) {
            return false;
        }
        
        // 删除当前批次
        boolean result = removeById(id);
        
        if (result) {
            // 更新其它批次的名称，保持连续性
            rebuildBatchNames(currentBatch.getProductId(), currentBatch.getMerchantId());
        }
        
        return result;
    }
    
    /**
     * 重建指定商品和商家的所有批次名称，保持连续性
     * @param productId 商品ID
     * @param merchantId 商家ID
     * @author xiaolic
     */
    private void rebuildBatchNames(Integer productId, Integer merchantId) {
        // 获取所有相关批次并按创建时间排序
        LambdaQueryWrapper<Batches> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Batches::getProductId, productId);
        queryWrapper.eq(Batches::getMerchantId, merchantId);
        queryWrapper.eq(Batches::getIsDeleted, 0);
        queryWrapper.orderByAsc(Batches::getSortOrder);
        queryWrapper.orderByAsc(Batches::getCreatedAt);
        List<Batches> batches = this.list(queryWrapper);
        
        // 重命名所有批次
        for (int i = 0; i < batches.size(); i++) {
            Batches batch = batches.get(i);
            String newBatchName = "第" + (i + 1) + "批";
            
            // 如果名称已经改变，则更新
            if (!newBatchName.equals(batch.getBatchName())) {
                batch.setBatchName(newBatchName);
                batch.setUpdatedAt(new Date());
                updateById(batch);
            }
        }
    }

    /**
     * 获取商品批次详情
     * @param id 批次ID
     * @return 批次详情
     */
    @Override
    public Batches getDetail(Long id) {
        LambdaQueryWrapper<Batches> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Batches::getId, id);
        queryWrapper.eq(Batches::getIsDeleted, 0);
        return getOne(queryWrapper);
    }

    /**
     * 检查号段是否重叠
     * @param startSerial 开始号段
     * @param endSerial 结束号段
     * @param productId 商品ID
     * @param merchantId 商家ID
     * @param batchId 排除的批次ID（更新时使用）
     * @return 是否重叠
     */
    @Override
    public Boolean checkSerialOverlap(Long startSerial, Long endSerial, Integer productId, Integer merchantId, Long batchId) {
        LambdaQueryWrapper<Batches> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.and(i -> i
                .and(j -> j.le(Batches::getStartSerial, startSerial).ge(Batches::getEndSerial, startSerial))
                .or(j -> j.le(Batches::getStartSerial, endSerial).ge(Batches::getEndSerial, endSerial))
                .or(j -> j.ge(Batches::getStartSerial, startSerial).le(Batches::getEndSerial, endSerial))
        );
        
        // 更新时排除当前批次
        if (batchId != null) {
            queryWrapper.ne(Batches::getId, batchId);
        }

        if (merchantId != null && merchantId > 0) {
            queryWrapper.eq(Batches::getMerchantId, merchantId);
        }

        if (productId != null && productId > 0) {
            queryWrapper.eq(Batches::getProductId, productId);
        }

        // 只检查未删除的批次
        queryWrapper.eq(Batches::getIsDeleted, 0);
        
        return batchesDao.selectCount(queryWrapper) > 0;
    }

    // 批次下载任务并发控制锁，key为批次ID
    private static final Map<Long, LockInfo> batchDownloadLocks = new ConcurrentHashMap<>();
    
    // 最大缓存时间，超过30分钟的锁将被清理
    private static final long LOCK_CACHE_TIMEOUT = 30 * 60 * 1000L; // 30分钟
    
    // 缓存清理线程，每10分钟清理一次过期锁
    static {
        Thread cleanupThread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(10 * 60 * 1000); // 10分钟清理一次
                    cleanupExpiredLocks();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "LockCleanupThread");
        cleanupThread.setDaemon(true); // 设置为守护线程，不阻止JVM退出
        cleanupThread.start();
    }
    
    /**
     * 存储锁信息的内部类
     */
    private static class LockInfo {
        private final ReentrantLock lock;
        private final AtomicInteger counter;
        private volatile long lastAccessTime;
        
        public LockInfo() {
            this.lock = new ReentrantLock();
            this.counter = new AtomicInteger(0);
            this.lastAccessTime = System.currentTimeMillis();
        }
        
        public ReentrantLock getLock() {
            return lock;
        }
        
        public AtomicInteger getCounter() {
            return counter;
        }
        
        public void updateAccessTime() {
            this.lastAccessTime = System.currentTimeMillis();
        }
        
        public boolean isExpired() {
            return System.currentTimeMillis() - lastAccessTime > LOCK_CACHE_TIMEOUT;
        }
    }
    
    /**
     * 清理过期的锁
     */
    private static synchronized void cleanupExpiredLocks() {
        batchDownloadLocks.entrySet().removeIf(entry -> {
            LockInfo lockInfo = entry.getValue();
            // 如果锁已过期且没有正在进行的下载任务，则移除
            return lockInfo.isExpired() && lockInfo.getCounter().get() == 0 && !lockInfo.getLock().isLocked();
        });
    }
    
    // 每批处理的数量
    private static final int BATCH_SIZE = 100;
    
    // 缓存临时目录的基础路径
    private static final String TEMP_DIR_BASE = System.getProperty("java.io.tmpdir") + "/qrcode_cache";
    
    /**
     * 批量下载批次二维码 - 旧版实现（已弃用）
     * 
     * @param batchId 批次ID
     * @param response HTTP响应对象
     * @throws IOException IO异常
     */
    /*
    @Override
    public void downloadBatchQrCodes(Long batchId, HttpServletResponse response) throws IOException {
        // 获取或创建批次锁信息
        LockInfo lockInfo = batchDownloadLocks.computeIfAbsent(batchId, k -> new LockInfo());
        lockInfo.updateAccessTime(); // 更新访问时间
        ReentrantLock batchLock = lockInfo.getLock();
        
        // 尝试获取锁，避免并发问题
        if (!batchLock.tryLock()) {
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write("当前批次正在被其他用户下载，请稍后再试");
            return;
        }
        
        try {
            // 检查是否有下载任务正在进行中
            AtomicInteger counter = lockInfo.getCounter();
            if (counter.incrementAndGet() > 1) {
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().write("当前批次正在被下载，请稍后再试");
                counter.decrementAndGet();
                return;
            }
            
            // 检查批次是否存在
            Batches batch = getDetail(batchId);
            if (batch == null) {
                throw new AppException("批次不存在");
            }
            
            // 创建缓存目录（如果不存在）
            File cacheDir = new File(TEMP_DIR_BASE);
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            
            // 重新实现文件下载的响应头设置
            response.reset(); // 清除之前的响应头
            response.setCharacterEncoding("UTF-8");
            
            // 使用最简单的字符集转换方式解决文件名乱码
            String filename = batch.getBatchName() + ".zip";
            try {
                // 这种方式在大多数浏览器上工作良好
                filename = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
            } catch (Exception e) {
                // 出错就使用原始名称
                log.error("文件名编码转换失败", e);
            }
            
            // 设置简单的Content-Disposition头
            response.setHeader("Content-Disposition", "attachment; filename=" + filename);
            
            // 添加其他必要的头
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Content-Type", "application/octet-stream;charset=UTF-8");
            response.setHeader("Connection", "close");
            
            // 使用缓冲输出流提高性能
            ZipOutputStream zipOut = null;
            try {
                // 不再使用双层缓冲，简化实现
                zipOut = new ZipOutputStream(response.getOutputStream());
                // 使用中等压缩级别，提高兼容性
                zipOut.setLevel(5); // 中等压缩级别
                
                // 查询该批次下所有未删除的可追溯项数量
                LambdaQueryWrapper<TraceableItems> countWrapper = new LambdaQueryWrapper<>();
                countWrapper.eq(TraceableItems::getBatchId, batchId);
                countWrapper.eq(TraceableItems::getIsDeleted, 0);
                int totalCount = traceableItemsService.count(countWrapper);
                
                if (totalCount == 0) {
                    throw new AppException("该批次下没有可追溯项");
                }
                
                // 分批处理可追溯项，避免内存占用过大
                int totalPages = (int) Math.ceil((double) totalCount / BATCH_SIZE);
                
                for (int pageNo = 0; pageNo < totalPages; pageNo++) {
                    // 使用PageHelper分页插件进行分页查询
                    LambdaQueryWrapper<TraceableItems> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(TraceableItems::getBatchId, batchId);
                    queryWrapper.eq(TraceableItems::getIsDeleted, 0);
                    queryWrapper.orderByAsc(TraceableItems::getSerialNumber);
                    
                    // 开启PageHelper分页
                    Page<TraceableItems> page = PageHelper.startPage(pageNo + 1, BATCH_SIZE);
                    List<TraceableItems> items = traceableItemsService.list(queryWrapper);
                    
                    if (CollUtil.isEmpty(items)) {
                        continue;
                    }
                    
                    // 处理当前批次的可追溯项
                    for (TraceableItems item : items) {
                        // 生成二维码图片，文件名为traceId
                        String fileName = item.getTraceId() + ".png";
                        File qrCodeFile = generateQrCodeFileWithCache(item);
                        
                        // 添加到ZIP文件 - 简化处理流程
                        if (qrCodeFile.exists() && qrCodeFile.isFile() && qrCodeFile.length() > 0) {
                            ZipEntry zipEntry = new ZipEntry(fileName);
                            zipOut.putNextEntry(zipEntry);
                            
                            FileInputStream fis = null;
                            try {
                                fis = new FileInputStream(qrCodeFile);
                                byte[] buffer = new byte[2048]; // 使用更小的缓冲区提高兼容性
                                int length;
                                while ((length = fis.read(buffer)) > 0) {
                                    zipOut.write(buffer, 0, length);
                                }
                                // 不再这里调用flush，避免影响文件结构
                            } finally {
                                if (fis != null) {
                                    try {
                                        fis.close();
                                    } catch (IOException e) {
                                        log.error("关闭文件输入流失败", e);
                                    }
                                }
                            }
                            zipOut.closeEntry();
                        } else {
                            log.warn("跳过无效二维码文件: " + qrCodeFile.getAbsolutePath());
                        }
                    }
                }
                
                // 只需要使用close()即可，不需要在这里调用flush()和finish()
                // 在JDK实现中，ZipOutputStream的close()会自动调用finish()
            } catch (Exception e) {
                log.error("创建ZIP文件时出错: ", e);
                // 异常时，尝试发送错误信息
                try {
                    response.reset();
                    response.setContentType("text/plain;charset=UTF-8");
                    response.getWriter().write("生成ZIP文件失败: " + e.getMessage());
                } catch (Exception ex) {
                    log.error("设置错误响应失败", ex);
                }
            } finally {
                // 关闭输出流
                if (zipOut != null) {
                    try {
                        zipOut.close(); // 这会自动调用finish()并完成ZIP文件结构
                    } catch (IOException e) {
                        log.error("关闭ZIP流失败", e);
                    }
                }
            }
        } finally {
            // 释放计数器和锁
            // 直接使用已经定义的lockInfo变量，而不是重新获取
            lockInfo.getCounter().decrementAndGet();
            lockInfo.updateAccessTime(); // 更新访问时间
            
            // 如果计数器为0，尝试清理锁资源
            if (lockInfo.getCounter().get() == 0) {
                // 查看是否有其他锁超时的，顺便清理
                cleanupExpiredLocks();
            }
            
            batchLock.unlock();
        }
    }
    */

    /*@Override
    public void downloadBatchQrCodes(Long batchId, HttpServletResponse response) throws Exception {
        // 检查批次是否存在
        Batches batch = getDetail(batchId);
        if (batch == null) {
            throw new AppException("批次不存在");
        }

        // 查询该批次下所有未删除的可追溯项
        LambdaQueryWrapper<TraceableItems> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TraceableItems::getBatchId, batchId);
        queryWrapper.eq(TraceableItems::getIsDeleted, 0);
        queryWrapper.orderByAsc(TraceableItems::getSerialNumber);
        List<TraceableItems> items = traceableItemsService.list(queryWrapper);

        if (CollUtil.isEmpty(items)) {
            throw new AppException("该批次下没有可追溯项");
        }

        response.setContentType("application/zip");
        response.setHeader("Content-disposition","attachment; filename=list.zip");

        OutputStream outputStream = response.getOutputStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);

        for (int i = 0; i < items.size(); i++) {
            TraceableItems item = items.get(i);
            BufferedImage image = generateQrCodePicWithTraceId(item.getQrCodeUrl(), item.getTraceId().toString());
            ZipEntry entry = new ZipEntry(item.getTraceId() + ".png");
            zipOutputStream.putNextEntry(entry);
            ImageIO.write(image, "png", zipOutputStream);
            zipOutputStream.flush();
        }
        zipOutputStream.close();
        outputStream.flush();
        outputStream.close();
    }*/

    /**
     * 批量下载批次二维码
     * @param batchId 批次ID
     * @param response HTTP响应对象，用于输出ZIP文件
     * @throws IOException IO异常
     */
    @Override
    public void downloadBatchQrCodes(Long batchId, HttpServletResponse response) throws IOException {
        // 检查批次是否存在
        Batches batch = getDetail(batchId);
        if (batch == null) {
            throw new AppException("批次不存在");
        }

        // 使用批次名称作为文件名
        String fileName = "【" + batch.getBatchName() + "】溯源码包.zip";
        
        // 使用RFC 5987编码规范解决文件名乱码问题
        // 这种方式不依赖于User-Agent，大部分浏览器都支持
        String encodedFilename = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20");
        String contentDisposition = "attachment; filename=\"" + encodedFilename + "\"; filename*=UTF-8''" + encodedFilename;
        
        response.setContentType("application/zip");
        response.setHeader("Content-disposition", contentDisposition);
        // 设置不缓存
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Expires", "0");
        
        // 获取输出流
        OutputStream outputStream = response.getOutputStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
        
        try {
            // 分批查询可追溯项，避免一次性加载全部数据到内存
            int pageSize = 50; // 每批处理的数量
            int currentPage = 1;
            boolean hasMoreItems = true;
            
            while (hasMoreItems) {
                // 分页查询可追溯项
                Page<TraceableItems> page = PageHelper.startPage(currentPage, pageSize);
                LambdaQueryWrapper<TraceableItems> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(TraceableItems::getBatchId, batchId);
                queryWrapper.eq(TraceableItems::getIsDeleted, 0);
                queryWrapper.orderByAsc(TraceableItems::getSerialNumber);
                List<TraceableItems> items = traceableItemsService.list(queryWrapper);
                
                if (CollUtil.isEmpty(items)) {
                    // 首次查询如果没有数据，则抛出异常
                    if (currentPage == 1) {
                        throw new AppException("该批次下没有可追溯项");
                    }
                    // 如果已经没有更多数据，则退出循环
                    hasMoreItems = false;
                    continue;
                }
                
                // 处理当前批次的可追溯项
                for (TraceableItems item : items) {
                    try {
                        // 生成二维码
                        BufferedImage image = generateQrCodePicWithTraceId(item.getQrCodeUrl(), item.getTraceId().toString());
                        
                        // 创建ZIP条目
                        ZipEntry entry = new ZipEntry(item.getTraceId() + ".png");
                        zipOutputStream.putNextEntry(entry);
                        
                        // 将图片写入ZIP流
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        ImageIO.write(image, "png", baos);
                        zipOutputStream.write(baos.toByteArray());
                        baos.close();
                        
                        // 关闭当前条目
                        zipOutputStream.closeEntry();
                        
                        // 刷新输出流，确保数据及时写入响应
                        zipOutputStream.flush();
                    } catch (Exception e) {
                        // 记录异常但继续处理其他项
                        log.error("生成二维码失败：traceId=" + item.getTraceId(), e);
                    }
                }
                
                // 检查是否还有更多数据
                if (items.size() < pageSize) {
                    hasMoreItems = false;
                } else {
                    currentPage++;
                }
            }
        } finally {
            // 确保无论发生什么异常，都会关闭ZIP流
            try {
                zipOutputStream.finish(); // 结束ZIP文件
                zipOutputStream.close(); // 关闭ZIP流
            } catch (Exception e) {
                log.error("关闭ZIP流失败", e);
            }
        }
    }


    /**
     * 获取当前管理员相关的所有未删除批次
     * @param admin 当前管理员
     * @param request 查询条件
     * @param pageParamRequest 分页参数
     * @return 批次信息列表
     */
    @Override
    public PageInfo<BatchesInfoResponse> getCurrentAdminBatches(SystemAdmin admin, BatchesFilterRequest request, PageParamRequest pageParamRequest) {
        // 获取当前管理员id（品牌id）
        Integer adminId = admin.getId();
        Integer brandId = admin.getBrandId();
        
        // 根据管理员id获取商户列表（状态为开启的商户）
        LambdaQueryWrapper<Merchant> merchantQueryWrapper = new LambdaQueryWrapper<>();
        if (brandId != null && brandId > 0) {
            merchantQueryWrapper.eq(Merchant::getBrandId, adminId);
        }
        merchantQueryWrapper.eq(Merchant::getIsSwitch, true); // 只查询开启状态的商户
        List<Merchant> merchants = merchantService.list(merchantQueryWrapper);
        
        if (merchants.isEmpty()) {
            return new PageInfo<>(); // 如果没有相关商户，直接返回空结果
        }
        
        // 获取所有商户id
        List<Integer> merchantIds = merchants.stream().map(Merchant::getId).collect(Collectors.toList());
        
        // 构建批次查询条件
        LambdaQueryWrapper<Batches> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Batches::getMerchantId, merchantIds); // 商户id列表
        queryWrapper.eq(Batches::getIsDeleted, 0); // 未删除的批次
        
        // 筛选条件：商户id
        if (request.getMerchantId() != null) {
            queryWrapper.eq(Batches::getMerchantId, request.getMerchantId());
        }
        
        // 筛选条件：商品id
        if (request.getProductId() != null) {
            queryWrapper.eq(Batches::getProductId, request.getProductId());
        }
        
        // 筛选条件：批次状态
        if (request.getStatus() != null) {
            queryWrapper.eq(Batches::getStatus, request.getStatus());
        }
        
        // 按更新时间倒序排序
        queryWrapper.orderByDesc(Batches::getUpdatedAt);
        
        // 分页查询
        Page<Batches> page = PageHelper.startPage(pageParamRequest.getPage(), pageParamRequest.getLimit());
        List<Batches> batchesList = batchesDao.selectList(queryWrapper);
        
        // 如果没有查到数据，直接返回空结果
        if (batchesList.isEmpty()) {
            return new PageInfo<>();
        }
        
        // 查询所有相关的商品id和商户id
        List<Integer> productIds = batchesList.stream().map(Batches::getProductId).distinct().collect(Collectors.toList());
        Map<Integer, Merchant> merchantMap = merchantService.getMerIdMapByIdList(merchantIds);
        
        // 查询商品信息
        Map<Integer, Product> productMap = new HashMap<>();
        if (!productIds.isEmpty()) {
            List<Product> products = productService.listByIds(productIds);
            productMap = products.stream().collect(Collectors.toMap(Product::getId, p -> p, (p1, p2) -> p1));
        }
        
        // 转换为响应对象
        List<BatchesInfoResponse> responseList = new ArrayList<>();
        for (Batches batch : batchesList) {
            BatchesInfoResponse response = new BatchesInfoResponse();
            BeanUtils.copyProperties(batch, response);
            
            // 设置商户名称
            Merchant merchant = merchantMap.get(batch.getMerchantId());
            if (merchant != null) {
                response.setMerchantName(merchant.getName());
            }
            
            // 设置商品名称
            Product product = productMap.get(batch.getProductId());
            if (product != null) {
                response.setProductName(product.getName());
            }
            
            // 设置状态描述
            switch (batch.getStatus()) {
                case 0:
                    response.setStatusDesc("待处理");
                    break;
                case 1:
                    response.setStatusDesc("生产中");
                    break;
                case 2:
                    response.setStatusDesc("已完成");
                    break;
                default:
                    response.setStatusDesc("未知状态");
            }
            
            responseList.add(response);
        }
        
        // 构建分页结果
        PageInfo<BatchesInfoResponse> pageInfo = new PageInfo<>();
        BeanUtils.copyProperties(page.toPageInfo(), pageInfo);
        pageInfo.setList(responseList);
        
        return pageInfo;
    }

    /**
     * 生成带溯源ID的二维码图片
     * @param qrCodeUrl
     * @param traceId
     * @return
     */
    @Override
    public BufferedImage generateQrCodePicWithTraceId(String qrCodeUrl, String traceId) {
        // 原始二维码的尺寸
        int qrCodeSize = 300; 
        // 标题区域高度（减小至30px）
        int titleHeight = 30;  
        // 最终图像的尺寸
        int totalWidth = qrCodeSize;
        int totalHeight = qrCodeSize + titleHeight; // 将标题高度添加到总高度中
        
        // 创建二维码
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.MARGIN, 1);

        // 生成二维码矩阵
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix;
        try {
            // 注意这里只使用qrCodeSize作为二维码尺寸
            bitMatrix = qrCodeWriter.encode(qrCodeUrl, BarcodeFormat.QR_CODE, qrCodeSize, qrCodeSize, hints);
        } catch (WriterException e) {
            log.error("二维码生成失败：traceId=" + traceId, e);
            throw new AppException("二维码生成失败");
        }

        // 创建最终总大小的图像（包含二维码和标题）
        BufferedImage combinedImage = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = combinedImage.createGraphics();
        
        // 设置背景为白色
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, totalWidth, totalHeight);
        
        // 绘制二维码部分
        for (int x = 0; x < qrCodeSize; x++) {
            for (int y = 0; y < qrCodeSize; y++) {
                if (bitMatrix.get(x, y)) {
                    graphics.setColor(Color.BLACK);
                    graphics.fillRect(x, y, 1, 1);
                }
            }
        }
        
        // 设置渲染提示以获得较好的体验
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        // 构造标题文本
        String titleText = "唯一溯源ID：" + traceId;
        
        // 加载字体
        Font font = null;
        
        // 1. 先尝试从资源文件中加载字体
        try {
            InputStream fontStream = this.getClass().getResourceAsStream("/fonts/simsun.ttc");
            if (fontStream != null) {
                // 从流创建字体
                font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
                // 设置字体大小和样式
                font = font.deriveFont(Font.BOLD, 16f);
                fontStream.close();
                log.info("成功从内部资源加载字体");
            } else {
                log.error("无法从资源加载字体文件，将尝试使用系统字体");
            }
        } catch (Exception e) {
            log.error("从资源文件加载字体失败，将尝试使用系统字体: " + e.getMessage());
        }
        
        // 2. 如果从资源加载失败，尝试使用系统字体
        if (font == null) {
            try {
                // 尝试使用常见中文字体
                String[] fontNames = {"Microsoft YaHei", "SimHei", "SimSun", "NSimSun", "FangSong", "KaiTi", "宋体", "WenQuanYi Micro Hei", "WenQuanYi Zen Hei", "Arial Unicode MS", "serif"};
                for (String fontName : fontNames) {
                    if (Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()).contains(fontName)) {
                        font = new Font(fontName, Font.BOLD, 16);
                        log.info("使用系统字体: " + fontName);
                        break;
                    }
                }
            } catch (Exception e) {
                log.error("获取系统字体失败", e);
            }
        }
        
        // 3. 如果上述方法都失败，使用Java默认字体
        if (font == null) {
            font = new Font(Font.SERIF, Font.BOLD, 16);
            log.error("使用Java默认字体，可能无法正确显示中文");
        }
        
        graphics.setFont(font);
        graphics.setColor(Color.BLACK);
        
        // 使标题文字居中并位于二维码下方，尽可能靠近二维码
        FontMetrics metrics = graphics.getFontMetrics();
        int textWidth = metrics.stringWidth(titleText);
        int textX = (totalWidth - textWidth) / 2;
        // 调整文字Y坐标，使其更靠近二维码
        int textY = qrCodeSize + metrics.getAscent() + 5; // 将文字向上移动，更靠近二维码

        graphics.drawString(titleText, textX, textY);
        graphics.dispose();

        return combinedImage;
    }

    /**
     * 生成带溯源ID的二维码图片
     * @param qrCodeUrl
     * @param traceId
     * @return
     */
    @Override
    public BufferedImage generateQrCodePic(String qrCodeUrl, String traceId) {
        // 原始二维码的尺寸
        int qrCodeSize = 300;
        // 标题区域高度（减小至30px）
        int titleHeight = 30;
        // 最终图像的尺寸
        int totalWidth = qrCodeSize;
        int totalHeight = qrCodeSize + titleHeight; // 将标题高度添加到总高度中

        // 创建二维码
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.MARGIN, 1);

        // 生成二维码矩阵
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix;
        try {
            // 注意这里只使用qrCodeSize作为二维码尺寸
            bitMatrix = qrCodeWriter.encode(qrCodeUrl, BarcodeFormat.QR_CODE, qrCodeSize, qrCodeSize, hints);
        } catch (WriterException e) {
            log.error("二维码生成失败：traceId=" + traceId, e);
            throw new AppException("二维码生成失败");
        }

        // 创建最终总大小的图像（包含二维码和标题）
        BufferedImage combinedImage = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = combinedImage.createGraphics();

        // 设置背景为白色
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, totalWidth, totalHeight);

        // 绘制二维码部分
        for (int x = 0; x < qrCodeSize; x++) {
            for (int y = 0; y < qrCodeSize; y++) {
                if (bitMatrix.get(x, y)) {
                    graphics.setColor(Color.BLACK);
                    graphics.fillRect(x, y, 1, 1);
                }
            }
        }

        // 设置渲染提示以获得较好的体验
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        // 构造标题文本
//        String titleText = "唯一溯源ID：" + traceId;

        // 加载字体
        Font font = null;

        // 1. 先尝试从资源文件中加载字体
        try {
            InputStream fontStream = this.getClass().getResourceAsStream("/fonts/simsun.ttc");
            if (fontStream != null) {
                // 从流创建字体
                font = Font.createFont(Font.TRUETYPE_FONT, fontStream);
                // 设置字体大小和样式
                font = font.deriveFont(Font.BOLD, 16f);
                fontStream.close();
                log.info("成功从内部资源加载字体");
            } else {
                log.error("无法从资源加载字体文件，将尝试使用系统字体");
            }
        } catch (Exception e) {
            log.error("从资源文件加载字体失败，将尝试使用系统字体: " + e.getMessage());
        }

        // 2. 如果从资源加载失败，尝试使用系统字体
        if (font == null) {
            try {
                // 尝试使用常见中文字体
                String[] fontNames = {"Microsoft YaHei", "SimHei", "SimSun", "NSimSun", "FangSong", "KaiTi", "宋体", "WenQuanYi Micro Hei", "WenQuanYi Zen Hei", "Arial Unicode MS", "serif"};
                for (String fontName : fontNames) {
                    if (Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()).contains(fontName)) {
                        font = new Font(fontName, Font.BOLD, 16);
                        log.info("使用系统字体: " + fontName);
                        break;
                    }
                }
            } catch (Exception e) {
                log.error("获取系统字体失败", e);
            }
        }

        // 3. 如果上述方法都失败，使用Java默认字体
        if (font == null) {
            font = new Font(Font.SERIF, Font.BOLD, 16);
            log.error("使用Java默认字体，可能无法正确显示中文");
        }

        graphics.setFont(font);
        graphics.setColor(Color.BLACK);

        // 使标题文字居中并位于二维码下方，尽可能靠近二维码
//        FontMetrics metrics = graphics.getFontMetrics();
//        int textWidth = metrics.stringWidth(titleText);
//        int textX = (totalWidth - textWidth) / 2;
        // 调整文字Y坐标，使其更靠近二维码
//        int textY = qrCodeSize + metrics.getAscent() + 5; // 将文字向上移动，更靠近二维码

//        graphics.drawString(titleText, textX, textY);
        graphics.dispose();

        return combinedImage;
    }

}
